﻿/*
 * Created by SharpDevelop.
 * User: yeang-shing.then
 * Date: 11/9/2012
 * Time: 5:52 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace KCLIMTrading.Mgmt
{
	/// <summary>
	/// Description of SortableBindingList.
	/// </summary>
	/// <seealso cref="http://msdn.microsoft.com/en-us/library/ms993236.aspx">MSDN Sortable Binding List</seealso>
	/// <seealso cref="http://stackoverflow.com/questions/5553100/how-to-enable-datagridview-sorting-when-user-clicks-on-the-column-header"></seealso>
	public class SortableBindingList<T> : BindingList<T>
	{
		private readonly Dictionary<Type, PropertyComparer<T>> comparers;
		private bool isSorted;
		private ListSortDirection listSortDirection;
		private PropertyDescriptor propertyDescriptor;

		public SortableBindingList()
			: base(new List<T>())
		{
			this.comparers = new Dictionary<Type, PropertyComparer<T>>();
		}

		public SortableBindingList(IEnumerable<T> enumeration)
			: base(new List<T>(enumeration))
		{
			this.comparers = new Dictionary<Type, PropertyComparer<T>>();
		}

		protected override bool SupportsSortingCore
		{
			get { return true; }
		}
		protected override bool IsSortedCore
		{
			get { return this.isSorted; }
		}
		protected override PropertyDescriptor SortPropertyCore
		{
			get { return this.propertyDescriptor; }
		}
		protected override ListSortDirection SortDirectionCore
		{
			get { return this.listSortDirection; }
		}
		protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
		{
			List<T> itemsList = (List<T>)this.Items;

			Type propertyType = property.PropertyType;
			PropertyComparer<T> comparer;
			if (!this.comparers.TryGetValue(propertyType, out comparer))
			{
				comparer = new PropertyComparer<T>(property, direction);
				this.comparers.Add(propertyType, comparer);
			}

			comparer.SetPropertyAndDirection(property, direction);
			itemsList.Sort(comparer);

			this.propertyDescriptor = property;
			this.listSortDirection = direction;
			this.isSorted = true;

			this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}
		protected override void RemoveSortCore()
		{
			this.isSorted = false;
			this.propertyDescriptor = base.SortPropertyCore;
			this.listSortDirection = base.SortDirectionCore;

			this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		protected override bool SupportsSearchingCore {
			get { return true; }
		}
		protected override int FindCore(PropertyDescriptor property, object key)
		{
			int count = this.Count;
			for (int i = 0; i < count;i++)
			{
				T element = this[i];
				if (property.GetValue(element).Equals(key))
					return i;
			}

			return -1;
		}
		/// <summary>
		/// Find value based on property in object.
		/// </summary>
		/// <param name="property"></param>
		/// <param name="key"></param>
		/// <seealso cref="http://msdn.microsoft.com/en-us/library/aa480736.aspx">Behind the Scenes: Improvements to Windows Forms Data Binding in the .NET Framework 2.0, Part 2</seealso>
		/// <returns></returns>
		public int Find(string property, object key)
		{
			PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
			PropertyDescriptor prop = properties.Find(property,true);
			if(prop == null)
				return -1;
			else
				return FindCore(prop,key);
		}
		
		private int lastSearchIndex = -1;
		private int LoopFindLike(PropertyDescriptor property, int start, string key)
		{
			for (int i = start; i < this.Count; i++)
			{
				T element = this[i];
				string original = property.GetValue(element).ToString();
				if(original.ToLower().Contains(key.ToLower()))
					return i;
			}
			
			return Math.Max(-1,lastSearchIndex);
		}
		protected int FindLikeCore(PropertyDescriptor property, string key)
		{
			int start = (lastSearchIndex+1)%this.Count;
			int found = LoopFindLike(property,start, key);
			if(found > -1)
			{
				if(lastSearchIndex == found)
				{
					lastSearchIndex = -1;
					FindLikeCore(property,key);
				}
				else
					lastSearchIndex = found;
			}

			return Math.Max(-1,lastSearchIndex);
		}
		public int FindLike(string property, string key)
		{
			PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
			PropertyDescriptor prop = properties.Find(property,true);
			if(prop == null)
				return -1;
			else
				return FindLikeCore(prop,key);
		}
	}
}
